package com.zzj.until;


import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 此类中封装一些常用的文件操作。 所有方法都是静态方法，不需要生成此类的实例， 为避免生成此类的实例，构造方法被申明为private类型的。
 *
 * @since 1.0
 */
public class FileTool {
    public static Logger logger = LogManager.getLogger(FileTool.class);

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param file 需要修改最后访问时间的文件。
     * @since 1.0
     */
    public static void touch(File file) {
        long currentTime = System.currentTimeMillis();
        if (!file.exists()) {
            logger.error("file not found:" + file.getName());
            logger.error("Create a new file:" + file.getName());
            try {
                if (file.createNewFile()) {
                    logger.info("Succeeded!");
                } else {
                    logger.error("Create file failed!");
                }
            } catch (IOException e) {
                logger.error("Create file failed!");
                logger.error(e.getMessage(),e);
            }
        }
        boolean result = file.setLastModified(currentTime);
        if (!result) {
            logger.error("touch failed: " + file.getName());
        }
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param fileName 需要修改最后访问时间的文件的文件名。
     * @since 1.0
     */
    public static void touch(String fileName) {
        File file = new File(fileName);
        touch(file);
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param files 需要修改最后访问时间的文件数组。
     * @since 1.0
     */
    public static void touch(File[] files) {
        for (int i = 0; i < files.length; i++) {
            touch(files[i]);
        }
    }

    /**
     * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
     * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
     *
     * @param fileNames 需要修改最后访问时间的文件名数组。
     * @since 1.0
     */
    public static void touch(String[] fileNames) {
        File[] files = new File[fileNames.length];
        for (int i = 0; i < fileNames.length; i++) {
            files[i] = new File(fileNames[i]);
        }
        touch(files);
    }

    /**
     * 判断指定的文件是否存在。
     *
     * @param fileName 要判断的文件的文件名
     * @return 存在时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean isFileExist(String fileName) {
        return new File(fileName).isFile();
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param file 要创建的目录(包括文件名e.g d://1//2//3//4.txt)
     * @return 完全创建成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean makeParaDirectory(File file) {
        File parent = file.getParentFile();
        if (parent != null) {
            return parent.mkdirs();
        }
        return false;
    }

    /**
     * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
     *
     * @param fileName 要创建的目录的目录名(包括文件名e.g d://1//2//3//4.txt)
     * @return 完全创建成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean makeParaDirectory(String fileName) {
        File file = new File(fileName);
        return makeParaDirectory(file);
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directory 要清空的目录
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false.
     * @since 1.0
     */
    public static boolean emptyDirectory(File directory) {
        boolean result = true;
        File[] entries = directory.listFiles();
        for (int i = 0; i < entries.length; i++) {
            if (!entries[i].delete()) {
                result = false;
            }
        }
        return result;
    }

    /**
     * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
     * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
     *
     * @param directoryName 要清空的目录的目录名
     * @return 目录下的所有文件都被成功删除时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean emptyDirectory(String directoryName) {
        pathValidate(directoryName);
        File dir = new File(directoryName);
        return emptyDirectory(dir);
    }

    /**
     * 删除文件
     * @param fileName
     * @return
     */
    public static boolean delFile(String fileName){
        File file=new File(fileName);
        boolean ret=true;
        if(file.exists()){
            ret=file.delete();
        }
        return ret;
    }

    /**
     * 删除文件
     * @param file
     * @return
     */
    public static boolean delFile(File file){
        boolean ret=true;
        if(file.exists()){
            ret=file.delete();
        }
        return ret;
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dirName 要删除的目录的目录名
     * @return 删除成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean deleteDirectory(String dirName) {
        return deleteDirectory(new File(dirName));
    }

    /**
     * 删除指定目录及其中的所有内容。
     *
     * @param dir 要删除的目录
     * @return 删除成功时返回true，否则返回false。
     * @since 1.0
     */
    public static boolean deleteDirectory(File dir) {
        if ((dir == null) || !dir.isDirectory()) {
            throw new IllegalArgumentException("Argument " + dir + " is not a directory. ");
        }

        File[] entries = dir.listFiles();
        int sz = entries.length;

        for (int i = 0; i < sz; i++) {
            if (entries[i].isDirectory()) {
                if (!deleteDirectory(entries[i])) {
                    return false;
                }
            } else {
                if (!entries[i].delete()) {
                    return false;
                }
            }
        }

        if (!dir.delete()) {
            return false;
        }
        return true;
    }

    /**
     * 移动文件，true 覆盖并删除文件， false 不覆盖并删除文件
     *
     * @param fromFile
     * @param toFile
     * @param cover
     * @return
     */
    public static boolean renameTo(File fromFile, File toFile, boolean cover) {
        boolean ret = false;
        try {
            ret = fromFile.renameTo(toFile);
            if (!ret) {
                if (cover) {
                    if (toFile.exists()) {
                        ret = toFile.delete();
                        ret = fromFile.renameTo(toFile);
                    }
                } else {
                    ret = fromFile.delete();
                }
            }
        } catch (Exception e) {
            ret = false;
            logger.error(e.getMessage(),e);
        }
        return ret;
    }

    public static boolean renameTo(String fromFile, String toFile, boolean cover) {
        File fr = new File(fromFile);
        File tof = new File(toFile);
        return renameTo(fr, tof, cover);
    }

    /**
     * 列出目录中的所有内容，包括其子目录中的内容。
     *
     * @param file   要列出的目录
     * @param filter 过滤器
     * @return 目录内容的文件数组。
     * @since 1.0
     */
    public static File[] listAll(File file, javax.swing.filechooser.FileFilter filter) {
        ArrayList list = new ArrayList();
        File[] files;
        if (!file.exists() || file.isFile()) {
            return null;
        }
        list(list, file, filter);
        files = new File[list.size()];
        list.toArray(files);
        return files;
    }

    /**
     * 将目录中的内容添加到列表。
     *
     * @param list   文件列表
     * @param filter 过滤器
     * @param file   目录
     */
    private static void list(ArrayList list, File file, javax.swing.filechooser.FileFilter filter) {
        if (filter.accept(file)) {
            list.add(file);
            if (file.isFile()) {
                return;
            }
        }
        if (file.isDirectory()) {
            File files[] = file.listFiles();
            for (int i = 0; i < files.length; i++) {
                list(list, files[i], filter);
            }
        }
    }

    /**
     * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。
     * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，
     * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。
     *
     * @param filePath 转换前的路径
     * @return 转换后的路径
     * @since 1.0
     */
    public static String toUNIXpath(String filePath) {
        return filePath.replace('\\', '/');
    }

    /**
     * 从文件名得到UNIX风格的文件绝对路径。
     *
     * @param fileName 文件名
     * @return 对应的UNIX风格的文件路径
     * @see #toUNIXpath(String filePath) toUNIXpath
     * @since 1.0
     */
    public static String getUNIXfilePath(String fileName) {
        File file = new File(fileName);
        return toUNIXpath(file.getAbsolutePath());
    }

    /**
     * 获取文件名，最后一个“.”的前面部分
     * @param file
     * @return
     */
    public static String getNamePart(File file){
        String fileName=file.getName();
        int point = StringUtils.lastIndexOf(fileName, ".");
        String namePart=StringUtils.substring(fileName,0,point);
        return namePart;
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param fileName 文件名
     * @return 文件名中的类型部分
     * @since 1.0
     */
    public static String getTypePart(String fileName) {
        int point = fileName.lastIndexOf('.');
        int length = fileName.length();
        if (point == -1 || point == length - 1) {
            return "";
        } else {
            return fileName.substring(point + 1, length);
        }
    }

    /**
     * 得到文件的类型。 实际上就是得到文件名中最后一个“.”后面的部分。
     *
     * @param file 文件
     * @return 文件名中的类型部分
     * @since 1.0
     */
    public static String getTypePart(File file) {
        return getTypePart(file.getName());
    }



    /**
     * 得到文件名中的父路径部分。 对两种路径分隔符都有效。 不存在时返回""。
     * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。
     *
     * @param fileName 文件名
     * @return 父路径，不存在或者已经是父目录时返回""
     * @since 1.0
     */
    public static String getPathPart(String fileName) {
        int point = getPathLsatIndex(fileName);
        int length = fileName.length();
        if (point == -1) {
            return "";
        } else if (point == length - 1) {
            int secondPoint = getPathLsatIndex(fileName, point - 1);
            if (secondPoint == -1) {
                return "";
            } else {
                return fileName.substring(0, secondPoint);
            }
        } else {
            return fileName.substring(0, point);
        }
    }

    /**
     * 得到路径分隔符在文件路径中首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName 文件路径
     * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathIndex(String fileName) {
        int point = fileName.indexOf('/');
        if (point == -1) {
            point = fileName.indexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置后首次出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName  文件路径
     * @param fromIndex 开始查找的位置
     * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathIndex(String fileName, int fromIndex) {
        int point = fileName.indexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.indexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName 文件路径
     * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathLsatIndex(String fileName) {
        int point = fileName.lastIndexOf('/');
        if (point == -1) {
            point = fileName.lastIndexOf('\\');
        }
        return point;
    }

    /**
     * 得到路径分隔符在文件路径中指定位置前最后出现的位置。 对于DOS或者UNIX风格的分隔符都可以。
     *
     * @param fileName  文件路径
     * @param fromIndex 开始查找的位置
     * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。
     * @since 1.0
     */
    public static int getPathLsatIndex(String fileName, int fromIndex) {
        int point = fileName.lastIndexOf('/', fromIndex);
        if (point == -1) {
            point = fileName.lastIndexOf('\\', fromIndex);
        }
        return point;
    }

    /**
     * 将文件名中的类型部分去掉。
     *
     * @param filename 文件名
     * @return 去掉类型部分的结果
     * @since 1.0
     */
    public static String trimType(String filename) {
        int index = filename.lastIndexOf(".");
        if (index != -1) {
            return filename.substring(0, index);
        } else {
            return filename;
        }
    }

    /**
     * 得到相对路径。 文件名不是目录名的子节点时返回文件名。
     *
     * @param pathName 目录名
     * @param fileName 文件名
     * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名
     * @since 1.0
     */
    public static String getSubpath(String pathName, String fileName) {
        int index = fileName.indexOf(pathName);
        if (index != -1) {
            return fileName.substring(index + pathName.length() + 1);
        } else {
            return fileName;
        }
    }

    /**
     * 检查给定目录的存在性 保证指定的路径可用，如果指定的路径不存在，那么建立该路径，可以为多级路径
     *
     * @param path d:/1/9/2/5
     * @return 真假值
     * @since 1.0
     */
    public static final boolean pathValidate(String path) {
        String[] arraypath = path.split("/");
        String tmppath = "";
        for (int i = 0; i < arraypath.length; i++) {
            tmppath += "/" + arraypath[i];
            File d = new File(tmppath.substring(1));
            if (!d.exists()) { // 检查Sub目录是否存在
                //System.out.println(tmppath.substring(1) + "目录不存在，创建！");
                logger.info(tmppath.substring(1) + "目录不存在，创建！");
                if (!d.mkdir()) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * 读取文件的内容 读取指定文件的内容
     *
     * @param path 为要读取文件的绝对路径
     * @return 以行读取文件后的内容。
     * @since 1.0
     */
    public static final String getFileContent(String path) throws IOException {
        String filecontent = "";
        try {
            File f = new File(path);
            if (f.exists()) {
                InputStreamReader fr = new InputStreamReader(new FileInputStream(path), "UTF-8");
                BufferedReader br = new BufferedReader(fr); // 建立BufferedReader对象，并实例化为br
                String line = br.readLine(); // 从文件读取一行字符串
                // 判断读取到的字符串是否不为空
                int i=0;
                while (line != null) {
                    filecontent += line + "\r\n";
                    line = br.readLine(); // 从文件中继续读取一行数据
                }
                br.close(); // 关闭BufferedReader对象
                fr.close(); // 关闭文件
            }

        } catch (IOException e) {
            throw e;
        }
        return filecontent;
    }

    /**
     * 根据文件夹路径获取文件列表
     * @param filePath
     * @return
     */
    public static File[] getFileList(String filePath){
        pathValidate(filePath);
        File file=new File(filePath);
        File[] fileList = file.listFiles();
        return fileList;
    }

    /**
     * 读取文件的内容 读取指定文件的内容，并将每一行转成List
     *
     * @param path 为要读取文件的绝对路径
     * @return 以行读取文件后的内容。
     * @since 1.0
     */
    public static final List<String> getFileContentAsList(String path) throws IOException {
        List<String> retList = new ArrayList<>();
        try {
            File f = new File(path);
            if (f.exists()) {
                InputStreamReader fr = new InputStreamReader(new FileInputStream(path), "UTF-8");
                // 建立BufferedReader对象，并实例化为br
                BufferedReader br = new BufferedReader(fr);
                // 从文件读取一行字符串
                String line = br.readLine();
                // 判断读取到的字符串是否不为空
                while (line != null) {
                    retList.add(line);
                    // 从文件中继续读取一行数据
                    line = br.readLine();
                }
                // 关闭BufferedReader对象
                br.close();
                // 关闭文件
                fr.close();
            }
        } catch (IOException e) {
            throw e;
        }
        return retList;
    }

    /**
     * 根据内容生成文件
     *
     * @param path          要生成文件的绝对路径，
     * @param modulecontent 文件的内容。
     * @param iscover       是否覆盖原先内容。true
     *                      不覆盖，false 覆盖
     * @return 真假值
     * @since 1.0
     */
    public static final boolean genModuleTpl(String path, String modulecontent, boolean iscover) throws IOException {

        path = getUNIXfilePath(path);
        String[] patharray = path.split("\\/");
        String modulepath = "";
        for (int i = 0; i < patharray.length - 1; i++) {
            modulepath += "/" + patharray[i];
        }
        File d = new File(modulepath.substring(1));
        if (!d.exists()) {
            if (!pathValidate(modulepath.substring(1))) {
                return false;
            }
        }
        try {
            // 建立FileWriter对象，并实例化fw
            FileWriter fw = new FileWriter(path, iscover);
            // 将字符串写入文件
            fw.write(modulecontent);
            fw.close();
        } catch (IOException e) {
            throw e;
        }
        return true;
    }

    /**
     * 拷贝文件
     *
     * @param in  源文件地址
     * @param out 目标文件地址
     * @return
     * @throws Exception
     */
    public static final boolean copyFile(File in, File out) {
        try {
            FileInputStream fis = new FileInputStream(in);
            FileOutputStream fos = new FileOutputStream(out);
            byte[] buf = new byte[1024];
            int i = 0;
            while ((i = fis.read(buf)) != -1) {
                fos.write(buf, 0, i);
            }
            fis.close();
            fos.close();
            return true;
        } catch (IOException ie) {
            logger.error(ie.getMessage(),ie);
            return false;
        }
    }

    /**
     * 拷贝文件
     *
     * @param infile  源文件地址
     * @param outfile 目标文件地址
     * @return
     * @throws Exception
     */
    public static final boolean copyFile(String infile, String outfile) {
        File in = new File(infile);
        File out = new File(outfile);
        return copyFile(in, out);
    }

    /**
     * 复制整个文件夹内容<br>
     * <b>如果文件夹不存在 则建立新文件夹<b>
     *
     * @param oldPath String 原文件路径 如：c:/fqf
     * @param newPath String 复制后路径 如：f:/fqf/ff
     * @return boolean
     */
    public static boolean copyFolder(String oldPath, String newPath) {
        try {
            // 如果文件夹不存在 则建立新文件夹
            pathValidate(newPath);
            File a = new File(oldPath);
            String[] file = a.list();
            File temp = null;
            for (int i = 0; i < file.length; i++) {
                if (oldPath.endsWith(File.separator)) {
                    temp = new File(oldPath + file[i]);
                } else {
                    temp = new File(oldPath + File.separator + file[i]);
                }

                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString());
                    byte[] b = new byte[1024 * 5];
                    int len;
                    while ((len = input.read(b)) != -1) {
                        output.write(b, 0, len);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                // 如果是子文件夹
                if (temp.isDirectory()) {
                    copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i]);
                }
            }
            return true;
        } catch (Exception e) {
            logger.info("复制整个文件夹内容操作出错");
            logger.error(e.getMessage(),e);
            return false;
        }
    }


    @Test
    public void test1() {
        // touch("d://1.txt");
        // touch(new String[]{"d://1.txt","d://3.txt"});
        // System.out.println(isFileExist("d://5.txt"));
        // System.out.println(makeDirectory("d://1//2//3//4"));
        // System.out.println(emptyDirectory("d://1//2"));
        // System.out.println(deleteDirectory("d://1//2"));
        // File[] files = listAll(new File("d://1//2"),new FileFilter() {
        //
        // @Override
        // public String getDescription() {
        // // TODO Auto-generated method stub
        // return null;
        // }
        //
        // @Override
        // public boolean accept(File f) {
        // if(f.getAbsolutePath().contains("4")){
        // return true;
        // }
        // return false;
        // }
        // });
        //
        // for(File file:files){
        // System.out.println(file.getAbsolutePath());
        // }

        // System.out.println(getFileName("d://1//2//5.txt"));
        // System.out.println(getFilePath("d://1//2//5.txt"));
        // System.out.println(getSubpath("d:/1", "d:/1/2/5.txt"));
        // System.out.println(pathValidate("d:/1/9/2/5"));
        try {
            //copyFolder("d:/1/9/2/", "d:/1/9/3");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
